5.15. Управляющие конструкции
Управляющие конструкции
Операторы
Условия
Циклы
Итерируемые таблицы
Lua поддерживает три базовых логических оператора: and, or, not. Их поведение следует парадигме короткого замыкания (short-circuit evaluation) и тесно связано с концепцией «истинности» значений в условиях отсутствия строгой булевой типизации. Ранее мы уже вкратце упомянули о том, что они заменяют собой некоторые символьные операторы, чем и упрощают язык, ведь мы словно пишем на английском языке.
not — унарный оператор, возвращающий true, если операнд является nil или false, и false — во всех остальных случаях. Результат всегда имеет тип boolean.
not nil --> true
not false --> true
not 0 --> false (0 считается истинным!)
not "" --> false
and — бинарный оператор, возвращающий первый аргумент, если он ложный (nil или false), иначе — второй. Не приводит результат к булеву типу, а возвращает фактическое значение последнего вычисленного операнда.
true and 42 --> 42
false and 42 --> false
nil and "hello" --> nil
or — возвращает первый истинный операнд или последний, если все ложны. Также не производит приведение типов.
nil or "default" --> "default"
false or 0 --> 0 (0 — истинно!)
"a" or "b" --> "a"
В Lua всё, кроме nil и false, считается истинным (truthy). Это включает числа (даже ноль), строки (даже пустые), таблицы, функции и т.д. Данная особенность позволяет использовать выражения как идиому для установки значения по умолчанию:
local value = input or "default"
Таким образом, операторы and и or являются не только логическими, но и средствами управления потоком данных, что характерно для функциональных и скриптовых парадигм.
Lua предоставляет две формы условной конструкции: if-then-else и её компактный аналог через and/or, хотя последний используется с осторожностью из-за семантических различий. Синтаксис if-then-else следующий:
if condition1 then
-- блок 1
elseif condition2 then
-- блок 2
else
-- блок 3
end
Условие (condition) интерпретируется как булево выражение согласно правилу truthiness.
Конструкция elseif не является макросом или сахаром — это часть синтаксиса, эквивалентная вложенному if, но более читаемая.
Блоки выполняются последовательно; после первого истинного условия остальные игнорируются.
Вложенность допускается без ограничений.
Пример:
if x > 0 then
print("positive")
elseif x < 0 then
print("negative")
else
print("zero")
end
Важно отметить, что в отличие от C-подобных языков, в Lua отсутствует тернарный оператор ?:. Однако его можно эмулировать через and/or, при соблюдении осторожности:
local result = (a > b) and x or y -- работает, если x не ложно
Однако если x == false или x == nil, выражение вернёт y, что может быть ошибкой. Поэтому рекомендуется использовать полноценный if.
Теперь поговорим о циклах.
Lua поддерживает три типа циклов: while, repeat-until и for (с двумя формами).
Цикл while выполняет тело, пока условие истинно. Проверка происходит до каждой итерации. Если условие ложно изначально, тело не выполнится ни разу.
while condition do
-- тело цикла
end
Цикл repeat-until представляет собой аналог do-while в других языках. Тело выполняется хотя бы один раз, проверка — в конце.
repeat
-- тело цикла
until condition -- выход при истине условия
Обратите внимание: until завершает цикл, когда условие становится истинным, что противоположно семантике while.
Цикл for бывает в двух формах - числовой и итерационный.
Числовой for используется для перебора диапазона целых чисел.
for var = start, stop, step do
-- тело
end
step по умолчанию равен 1.
Все три параметра вычисляются единожды перед началом цикла.
Переменная var локальна по отношению к циклу (это важно!).
for i = 1, 10, 2 do
print(i) -- 1, 3, 5, 7, 9
end
Итерационный for (generic for) предназначен для обхода коллекций с помощью итераторов. Наиболее часто используется с таблицами.
for key, value in iterator, table, start_state do
-- тело
end
На практике применяются стандартные итераторы:
- pairs(tbl) — перебирает все элементы таблицы (в произвольном порядке).
- ipairs(tbl) — перебирает элементы с целочисленными ключами от 1 до первого nil.
for k, v in pairs(mytable) do
print(k, v)
end
Использование pairs и ipairs требует понимания различий: pairs негарантированный, а ipairs последовательный.
Механизм generic for основан на протоколе итераций: итератор — это функция, возвращающая на каждом шаге новые значения ключа/значения, пока не вернёт nil.
t = {10, 20, nil, 40, [5] = 50, name = "Alice"}
for i, v in ipairs(t) do
print(i, v) -- 1:10, 2:20 (остановка на nil)
end
for k, v in pairs(t) do
print(k, v) -- может напечатать 1,2,5,"name" в любом порядке
end
При модификации таблицы во время итерации поведение pairs и ipairs не определено. Lua не гарантирует безопасность итерации при изменении структуры.